﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using ScrumFactory.Composition.ViewModel;
using ScrumFactory.Composition;
using ScrumFactory.Windows.Helpers;
using ScrumFactory.Services;


namespace ScrumFactory.ProjectsList.ViewModel {    

    /// <summary>
    /// Implements the Projects List ViewModel.
    /// </summary>
    [Export(typeof(IProjectsListViewModel))]    
    [Export(typeof(IPanelViewModel))]    
    public class ProjectsListViewModel : BasePanelViewModel, IPanelViewModel, IProjectsListViewModel, INotifyPropertyChanged {

        private Project selectedProject;
        private string searchFilterText;
        private ProjectStatusFilter statusFilter;

        private  DelayAction delayFilter;


        private System.Windows.Data.CollectionViewSource projectsViewSource;
        
        private IEventAggregator aggregator { get; set; }
        private IProjectsService projectsService { get; set; }
        private IBackgroundExecutor executor { get; set; }
                

        [ImportingConstructor()]
        public ProjectsListViewModel(
            [Import(typeof(IEventAggregator))] IEventAggregator aggregator,
            [Import(typeof(IProjectsService))] IProjectsService projectsService,
            [Import(typeof(IBackgroundExecutor))] IBackgroundExecutor executor) {

            this.aggregator = aggregator;
            this.projectsService = projectsService;
            this.executor = executor;

            projectsViewSource = new System.Windows.Data.CollectionViewSource();
            projectsViewSource.Filter += new System.Windows.Data.FilterEventHandler(FilteredProjects_Filter);
            delayFilter = new DelayAction(1000, new DelayAction.ActionDelegate(() => { FilteredProjects.Refresh(); }));

            aggregator.Subscribe(ScrumFactoryEvent.OpenCreateProjectWindow, OpenCreateProjectWindow);

            RefreshProjectListCommand = new DelegateCommand(LoadProjectList);
            LoadOnceProjectListCommand = new DelegateCommand(() => {
                if(!this.DataLoadedBefore)
                    this.LoadProjectList();
            });
            
        }

        /// <summary>
        /// Opens the create project window.
        /// </summary>
        private void OpenCreateProjectWindow() {            
            CreateProjectViewModel vm = new CreateProjectViewModel(executor, aggregator, projectsService);
            vm.Show();
            if (vm.NewProject != null) {                
                SelectedProject = vm.NewProject;
                aggregator.Publish<Project>(ScrumFactoryEvent.ProjectCreated, SelectedProject);                
            }
        }

        /// <summary>
        /// Loads the project list from the service.
        /// </summary>
        private void LoadProjectList() {
            IsLoadingData = true;
            this.Projects = new ObservableCollection<Project>();
            executor.StartBackgroundTask<ICollection<Project>>(() => {

                if (StatusFilter == ProjectStatusFilter.ALL_PROJECTS)
                    return projectsService.GetAllProjects();

                if (StatusFilter == ProjectStatusFilter.RUNNING_PROJECTS)
                    return projectsService.GetRunningProjects();

                return null;
            },
            projects => {
                if (projects != null) {
                    this.Projects.Clear();
                    foreach (var project in projects) {
                        this.Projects.Add(project);
                    }
                    
                    projectsViewSource.Source = Projects;
                    
                }
                IsLoadingData = false;
                
                OnPropertyChanged("FilteredProjects");
            });
        }

       
        /// <summary>
        /// Handles the Filter event of the FilteredProjects control.
        /// Only projects that fits the status filter and the search filter text are accepted.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Data.FilterEventArgs"/> instance containing the event data.</param>
        private void FilteredProjects_Filter(object sender, System.Windows.Data.FilterEventArgs e) {
            Project item = e.Item as Project;
            if (item == null) {
                e.Accepted = false;
                return;
            }

            if (string.IsNullOrEmpty(SearchFilterText)) {
                e.Accepted = true;
                return;
            }

            string itemText = item.ProjectNumber + " " + item.ProjectName + " " + item.ClientName;

            string[] words = SearchFilterText.ToUpper().Split(new char[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries);

            e.Accepted = StringContainsAll(itemText, words);

        }

        /// <summary>
        /// Verifies if the text contains all words.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <param name="words">The words.</param>
        /// <returns>True if the text contains all words.</returns>
        private bool StringContainsAll(string text, string[] words) {
            if (words == null)
                return true;
            bool containsAll = true;
            foreach (string word in words) {
                containsAll = containsAll && text.ToUpper().Contains(word.ToUpper());
                if (!containsAll)
                    break;
            }

            return containsAll;
        }

        #region IPanelViewModel Members

        /// <summary>
        /// Gets the name of the panel.
        /// </summary>
        /// <value>The name of the panel.</value>
        public string PanelName {
            get {
                return Properties.Resources.My_projects;
            }
        }

        /// <summary>
        /// Gets the panel placement.
        /// </summary>
        /// <value>The panel placement.</value>
        public PanelPlacements PanelPlacement {
            get { return PanelPlacements.DockPanelLeft; }
        }

        /// <summary>
        /// Gets the panel Unique id.
        /// </summary>
        /// <value>The panel Unique id.</value>
        public string PanelUId {
            get {
                return "PROJECTS_LIST";
            }
        }

        /// <summary>
        /// Gets the panel display order when it is displayed at the view.
        /// </summary>
        /// <value>The panel display order.</value>
        public int PanelDisplayOrder {
            get {
                return 100;
            }
        }

        /// <summary>
        /// Gets the view.
        /// </summary>
        /// <value>The view.</value>
        object IPanelViewModel.View {
            get {
                return this.View;
            }
        }

        #endregion

        #region IProjectsListViewModel Members


        /// <summary>
        /// Gets or sets the view.
        /// </summary>
        /// <value>The view.</value>
        [Import(typeof(IProjectsListView))]
        public IProjectsListView View { get; set; }

        /// <summary>
        /// Gets the filtered projects.
        /// </summary>
        /// <value>The filtered projects.</value>
        public ICollectionView FilteredProjects {
            get {
                return projectsViewSource.View;
            }
        }       

        /// <summary>
        /// Gets the projects.
        /// </summary>
        /// <value>The projects.</value>
        public ObservableCollection<Project> Projects { get; private set; }

        /// <summary>
        /// Gets or sets the status filter.
        /// </summary>
        /// <value>The status filter.</value>
        public ProjectStatusFilter StatusFilter {
            get {
                return this.statusFilter;
            }
            set {
                this.statusFilter = value;
                OnPropertyChanged("StatusFilter");
                RefreshProjectListCommand.Execute(null);
            }
        }

        /// <summary>
        /// Gets the selected project.
        /// </summary>
        /// <value>The selected project.</value>
        public Project SelectedProject {
            get {
                return this.selectedProject;
            }
            set {
                selectedProject = value;
                OnPropertyChanged("SelectedProject");
                if (value == null)
                    return;

                executor.StartBackgroundTask(
                    () => {
                        selectedProject = projectsService.GetProject(value.ProjectUId);
                    },
                    () => {
                        aggregator.Publish<Project>(ScrumFactoryEvent.ViewProjectDetails, SelectedProject);
                    });                                
            }
        }


        /// <summary>
        /// Gets or sets the search filter text.
        /// </summary>
        /// <value>The search filter text.</value>
        public string SearchFilterText {
            get {
                return this.searchFilterText;
            }
            set {
                this.searchFilterText = value;
                OnPropertyChanged("SearchFilterText");
                delayFilter.StartAction();
            }
        }

        /// <summary>
        /// Gets the load once project list command.
        /// This command is executed only once, when the View is showed.
        /// </summary>
        /// <value>The load once project list command.</value>
        public System.Windows.Input.ICommand LoadOnceProjectListCommand { get; private set;}

        /// <summary>
        /// Gets the create project command.
        /// </summary>
        /// <value>The create project command.</value>
        public System.Windows.Input.ICommand CreateProjectCommand { get; private set; }

        /// <summary>
        /// Gets the refresh project list command.
        /// </summary>
        /// <value>The refresh project list command.</value>
        public System.Windows.Input.ICommand RefreshProjectListCommand { get; private set; }

        #endregion
    }
}
